<?php
/**
* $Id$
* Class ErrorHandler
* 
* @author Hung Nguyen, Flexica Solution
*/
require_once('ApplicationException.php');
class ErrorHandler extends CErrorHandler
{
    //Severity level
    const NO_ERROR = 0;
    const GENERIC_ERROR = 1;
    
    const CAN_RETRY_CONTINUE = 10;
    const CAN_RETRY_STOP_USER_SERVICE = 101;
    const CAN_RETRY_STOP_USER_SESSION = 102;
    
    const NO_RETRY_CONTINUE = 20;            
    const NO_RETRY_STOP_USER_SERVICE = 201;
    const NO_RETRY_STOP_USER_SESSION = 202;
    
    const EXTERNAL_SERVICE_ERROR = 30;
    
    const ALERT_USER_ATTACK = 500;
    const USER_ATTACK = 501;
    
    const APPLICATION_SHUT_DOWN = 1000;
    
    public static $mode;
      
    
    //Error stack
    private $errors = array();
    //Current process highest error severity level
    private $severity = self::NO_ERROR;
    
    public function setmode($value){
        self::$mode = $value;
    }
    
    /**
    * Log error/exception into the stack for later handling
    * 
    * @param ApplicationException $error. USER_EXCEPTION (level 3) error will be dumped to user when current controll calls showUserError()
    * @param mixed $severity 
    */
    public static function logError($error, $severity = ErrorHandler::GENERIC_ERROR){
        if (is_string($error))
            $error = new ApplicationException($error, ApplicationException::USER_EXCEPTION);
            
        $appErrorHandler = Yii::app()->getErrorHandler();
        $appErrorHandler->errors[] = $error;
        //Log error with user information
        Yii::log($error->getErrorMessage(), 'error');
        
        $appErrorHandler->setSeverity($severity);
    }
    
    /**
    * Log an array of error/exception into the stack for later handling
    * 
    * @param array $errors
    * @param mixed $severity
    */
    public static function logErrors($errors, $severity = ErrorHandler::GENERIC_ERROR){
        foreach($errors as $error)
            self::logError($error, $severity);
    }
    
    /**
    * Get exceptions to display to user
    * 
    * While application in development mode, all logged exceptions will be returned
    * 
    */
    public static function getUserExceptions(){
        if (self::$mode == 'debug')
            return Yii::app()->getErrorHandler()->errors;
            
        $errors = Yii::app()->getErrorHandler()->errors;
        $userErrors = array();
        $c = count($errors);
        for($i = 0; $i < $c; $i++){
            $e = $errors[$i];
            if ($e instanceof ApplicationException && $e->Category == ApplicationException::USER_EXCEPTION){
                $userErrors[] = $e;
            }
        }
        return $userErrors;
    }
    
    public function setSeverity($severity){
        if ($this->severity >= $severity)
            return;
            
        $this->severity = $severity;
        $this->safeUser();
    }
    
    /**
    * protect user from losing data and defend if attack is detected
    * 
    */
    protected function safeUser(){
        switch($this->severity){
            case self::CAN_RETRY_STOP_USER_SERVICE:
                $this->processErrorStack();
                break;
            case self::CAN_RETRY_STOP_USER_SESSION:
                $this->processErrorStack();
                $this->logUserOut();
                break;
            case self::NO_RETRY_STOP_USER_SERVICE:
                $this->processErrorStack();
                break;
            case self::NO_RETRY_STOP_USER_SESSION:
                $this->processErrorStack();
                $this->logUserOut();
                break;
            //TODO: For possible attack detected or detected acttack, blacklist user and process app defend
            default:
                break;
        } 
    }
    
    /**
    * Process error stack and do reporting
    * 
    */
    public function processErrorStack(){
        if ($this->severity == self::NO_ERROR) return;
        if (Yii::app() instanceof CApplicationComponent){
            foreach($this->errors as $e){
                //Send email if needed
            }        
        }elseif(Yii::app() instanceof CConsoleApplication){
            echo "Error(s): \n";
            foreach($this->errors as $e){
                echo $e->getErrorMessage()."\n";
            }                
        }
    }
    
    /**
    * Override CErrorHandler handle event to process error stack
    * 
    * @param mixed $event
    */
    public function handle($event){
        //Process the error stack first
        $this->processErrorStack();
        while(@ob_end_flush());
        //Handle error event as usual
        parent::handle($event);
    }
    
    
}
?>
